#include "G_u8g2.h"

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>


//定义u8g2结构体
static u8g2_t u8g2;
//坐标变量
static u8g2_uint_t x,y;
//printf缓存区
static char dispBuf[128];

//IIC引脚初始化
static void IIC_Init(void);

//硬件层驱动回调函数
static uint8_t u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);

//延时函数
static void delay_ms(uint32_t milliseconds) ;
/**
  * @brief  u8g2初始化
  * @param  None
  * @retval None
  */
void appU8g2Init(void){
		
	//IIC引脚初始化
	IIC_Init(); 
	delay_ms(20);
	

	u8g2_Setup_ssd1306_i2c_128x64_noname_f(&u8g2, 
																				U8G2_R0, 
																				u8x8_byte_sw_i2c, 
																				u8x8_gpio_and_delay);  // 初始化 u8g2 结构体
	u8g2_InitDisplay(&u8g2);
	u8g2_SetPowerSave(&u8g2, 0);
	u8g2_ClearBuffer(&u8g2);
	u8g2_SendBuffer(&u8g2);
}
/**
  * @brief  清除缓存区
  * @param  None
  * @retval None
  */
void GU8g2_clearBuffer(void){
	u8g2_ClearBuffer(&u8g2);  
}

/**
  * @brief  设置字体背景颜色模式
  * @param  num:启用(1)透明模式  禁用(0)透明模式
  * @retval None
  */
void GU8g2_setFontModer(uint8_t num){
	 u8g2_SetFontMode(&u8g2,num);
}

/**
  * @brief  设置字体
  * @param  font:字体
  * @retval None
  */
void GU8g2_setFont(const uint8_t * font){
	 u8g2_SetFont(&u8g2,font);
}

/**
  * @brief  设置坐标位置
  * @param  _x:x轴坐标 _y:y轴坐标 
  * @retval None
  */
void GU8g2_setCursor(u8g2_uint_t _x,u8g2_uint_t _y){
	 x = _x;
	 y = _y;
}


/**
  * @brief  在指定坐标下显示字符
  * @param  font:字体
  * @retval None
  */
void GU8g2_printf(const char *format, ...){
	  va_list args;
    va_start(args, format);

    // 使用vsnprintf首先获取格式化后的字符串长度
    int length = vsnprintf(NULL, 0, format, args);
    va_end(args);


    // 再次调用vsnprintf来格式化字符串并保存到分配的内存中
    va_start(args, format);
    vsnprintf(dispBuf, length + 1, format, args);
    va_end(args);

    u8g2_DrawStr(&u8g2, x, y,dispBuf);
}

/**
* @brief  发送缓存区
  * @param  None
  * @retval None
  */
void GU8g2_sendBuffer(void){
 	u8g2_SendBuffer(&u8g2);
}

/**
  * @brief  设置字体方向
  * @param  dir:0	0 degree	  1	90 degree  2	180 degree  3	270 degree
  * @retval None
  */
void GU8g2_SetFontDirection(uint8_t dir){
	u8g2_SetFontDirection(&u8g2,dir);
}

/**
  * @brief  输出字符串
  * @param  
  * @retval None
  */
void GU8g2_drawStr(u8g2_uint_t _x, u8g2_uint_t _y, const char *str){
	u8g2_DrawStr(&u8g2,_x,_y,str);
}

/**
  * @brief  绘制水平线
  * @param  
  * @retval None
  */
void GU8g2_drawHLine(u8g2_uint_t _x, u8g2_uint_t _y, u8g2_uint_t len){
	u8g2_DrawHLine(&u8g2,_x,_y,len);
}


/**
  * @brief   绘制竖直线
  * @param  
  * @retval None
  */
void GU8g2_drawVLine(u8g2_uint_t _x, u8g2_uint_t _y, u8g2_uint_t len){
	u8g2_DrawVLine(&u8g2,_x,_y,len);
}
/**
  * @brief   获取显示器的高度
  * @param  
  * @retval None
  */
u8g2_uint_t GU8g2_getDisplayHeight(void){
	
	return u8g2_GetDisplayHeight(&u8g2);
}
/**
  * @brief  画实心方形
  * @param  
  * @retval None
  */
void GU8g2_drawBox(u8g2_uint_t _x, u8g2_uint_t _y, u8g2_uint_t w, u8g2_uint_t h){
	u8g2_DrawBox(&u8g2,_x,_y,w,h);
}

/**
 * 设置位图模式（定义drawXBM方法是否绘制背景颜色）
 * @param is_transparent
 *         0 绘制背景颜色，不透明，默认是该值
 *         1 不绘制背景颜色，透明
 * @Note 关联方法 drawXBM
 */
void GU8g2_setBitmapMode(uint8_t is_transparent){
	u8g2_SetBitmapMode(&u8g2,is_transparent);
}
/**
 * 设置绘制颜色（暂时还没有具体去了解用法）
 */
void GU8g2_setDrawColor(uint8_t color){

	u8g2_SetDrawColor(&u8g2,color);
}

/**
 * 绘制图像
 * @param x 左上角坐标x
 * @param y 左上角坐标y
 * @param w 图形宽度
 * @param h 图形高度
 * @param bitmap 图形内容
 * @Note 关联方法 setBitmapMode
 */
void GU8g2_drawXBMP(u8g2_uint_t _x, u8g2_uint_t _y, u8g2_uint_t w, u8g2_uint_t h, const uint8_t *bitmap){

	u8g2_DrawXBMP(&u8g2,_x,_y,w,h,bitmap);

}
/**
 * 画空心方形，左上角坐标为(x,y),宽度为w，高度为h
 * @param x 左上角的x坐标
 * @param y 左上角的y坐标
 * @param w 方形的宽度
 * @param h 方形的高度
 * @Note 关联方法 setDrawColor
 */
void GU8g2_drawFrame(u8g2_uint_t _x, u8g2_uint_t _y, u8g2_uint_t w, u8g2_uint_t h){

	u8g2_DrawFrame(&u8g2,_x,_y,w,h);

}

/**
 * 绘制UTF8编码的字符串
 * @param x 字符串在屏幕上的左下角x坐标
 * @param y 字符串在屏幕上的左下角y坐标
 * @param s 需要绘制的UTF-8编码字符串
 * @return 返回字符串的长度
 */
void GU8g2_drawUTF8(u8g2_uint_t _x, u8g2_uint_t _y, const char *s){

	u8g2_DrawUTF8(&u8g2,_x,_y,s);

}

/**
 * 画实心圆，圆心坐标为(x0,y0),半径为rad
 * @param x0 圆点的x坐标
 * @param y0 圆点的y坐标
 * @param rad 圆形的半径
 * @param opt 圆形选项
 *        U8G_DRAW_ALL 整个圆
 *        U8G2_DRAW_UPPER_RIGHT 右上部分的圆弧
 *        U8G2_DRAW_UPPER_LEFT  左上部分的圆弧
 *        U8G2_DRAW_LOWER_LEFT  左下部分的圆弧
 *        U8G2_DRAW_LOWER_RIGHT 右下部分的圆弧
 *       选项可以通过 | 操作符来组合
 * @Note 关联方法 drawCircle setDrawColor
 */

void GU8g2_drawDisc( u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad, uint8_t option){

	u8g2_DrawDisc(&u8g2,x0,y0,rad,option);

}

/**
 * 画空心圆，圆心坐标为(x0,y0),半径为rad
 * @param x0 圆点的x坐标
 * @param y0 圆点的y坐标
 * @param rad 圆形的半径
 * @param opt 圆形选项
 *        U8G_DRAW_ALL 整个圆
 *        U8G2_DRAW_UPPER_RIGHT 右上部分的圆弧
 *        U8G2_DRAW_UPPER_LEFT  左上部分的圆弧
 *        U8G2_DRAW_LOWER_LEFT  左下部分的圆弧
 *        U8G2_DRAW_LOWER_RIGHT 右下部分的圆弧
 *        选项可以通过 | 操作符来组合
 * @Note 关联方法 drawDisc setDrawColor
 */
void GU8g2_drawCircle( u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad, uint8_t option){

	u8g2_DrawCircle(&u8g2,x0,y0,rad,option);

}

/**
 * 绘制圆角空心方形，左上角坐标为(x,y),宽度为w，高度为h，圆角半径为r
 * @param x 左上角的x坐标
 * @param y 左上角的y坐标
 * @param w 方形的宽度
 * @param h 方形的高度
 * @param r 圆角半径
 */
void GU8g2_drawRFrame(u8g2_uint_t _x, u8g2_uint_t _y, u8g2_uint_t w, u8g2_uint_t h, u8g2_uint_t r){

	u8g2_DrawRFrame(&u8g2,_x,_y,w,h,r);

}

/**
 * 绘制圆角实心方形，左上角坐标为(x,y),宽度为w，高度为h，圆角半径为r
 * @param x 左上角的x坐标
 * @param y 左上角的y坐标
 * @param w 方形的宽度
 * @param h 方形的高度
 * @param r 圆角半径
 */

void GU8g2_drawRBox(u8g2_uint_t _x, u8g2_uint_t _y, u8g2_uint_t w, u8g2_uint_t h, u8g2_uint_t r){

	u8g2_DrawRBox(&u8g2,_x,_y,w,h,r);

}

/**
 * 绘制线，从坐标(x0,y0) 到(x1,y1)
 * @param x0 端点0的x坐标
 * @param y0 端点0的y坐标
 * @param x1 端点1的x坐标
 * @param y1 端点1的y坐标
 * @Note 关联方法 setDrawColor
 */
 void GU8g2_drawLine(u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t x1, u8g2_uint_t y1){
 
	u8g2_DrawLine(&u8g2,x0,y0,x1,y1);
 }
 
 /**
 * 绘制实心三角形，定点坐标分别为(x0,y0),(x1,y1),(x2,y2)
 */
void GU8g2_drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2) {
	u8g2_DrawTriangle(&u8g2,x0,y0,x1,y1,x2,y2);

}

 /**
 * 设置字体的参考高度为“扩展文本”模式，通常用于确保文本的顶部和底部有足够的间距，这在显示多行文本时尤其有用。
 */
void GU8g2_SetFontRefHeightExtendedText(void) {
	u8g2_SetFontRefHeightExtendedText(&u8g2);
}

 /**
 *  函数用来设置文字在显示时以顶部为基准线，这在进行图形界面布局时特别有用，尤其是当你需要确保所有文本行的顶部对齐时。
 */
void GU8g2_SetFontPosTop(void) {
	u8g2_SetFontPosTop(&u8g2);
}

void GU8g2_drawGlyph(u8g2_uint_t x, u8g2_uint_t y, uint16_t encoding) {
	u8g2_DrawGlyph(&u8g2,x,y,encoding);

}



/**
  * @brief  IIC引脚初始化
  * @param  None
  * @retval None
  */
static void IIC_Init(void)
{			
  GPIO_InitTypeDef  GPIO_InitStructure;

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟

  //GPIOB6,B7初始化设置
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
	
	GPIO_SetBits(GPIOB, GPIO_Pin_6|GPIO_Pin_7);
}
/**
  * @brief  基于循环的延时函数，以微秒为单位
  * @param  延时的微秒数
  * @retval None
  */
static void delay_us(uint32_t x_us) {
    // 计算每个微秒的周期数
    uint32_t ticks = 20;

    // 基于循环的延时
    for (uint32_t i = 0; i < x_us; i++) {
        // 等待一个微秒
        for (uint32_t j = 0; j < ticks; j++) {
            // 空操作
            __NOP();
        }
    }
}

/**
  * @brief  基于循环的延时函数，以毫秒为单位
  * @param  milliseconds: 延时的毫秒数
  * @retval None
  */
static void delay_ms(uint32_t milliseconds) {

    // 基于循环的延时
    for (uint32_t i = 0; i < milliseconds * 1000; i++) {
      delay_us(1);
    }
}

/**
  * @brief  硬件层驱动回调函数
  * @param  u8x8: 操作句柄
	* @param  msg: 消息
	* @param  arg_int: 参数
	* @param  arg_ptr: 参数指针
  * @retval uint8_t：1为成功
  */
uint8_t u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
    switch (msg) {
        case U8X8_MSG_DELAY_100NANO: // delay arg_int * 100 nano seconds
            break;
        case U8X8_MSG_DELAY_10MICRO: // delay arg_int * 10 micro seconds
            break;
        case U8X8_MSG_DELAY_MILLI:   // delay arg_int * 1 milli second
            delay_ms(1);
            break;
        case U8X8_MSG_DELAY_I2C:     // arg_int is the I2C speed in 100KHz, e.g. 4 = 400 KHz
            delay_us(5);
            break;                    // arg_int=1: delay by 5us, arg_int = 4: delay by 1.25us
        case U8X8_MSG_GPIO_I2C_CLOCK: // arg_int=0: Output low at I2C clock pin
            arg_int ? GPIO_SetBits(GPIOB, GPIO_Pin_6) : GPIO_ResetBits(GPIOB, GPIO_Pin_6);  
            break;                    // arg_int=1: Input dir with pullup high for I2C clock pin
        case U8X8_MSG_GPIO_I2C_DATA:  // arg_int=0: Output low at I2C data pin
            arg_int ? GPIO_SetBits(GPIOB, GPIO_Pin_7) : GPIO_ResetBits(GPIOB, GPIO_Pin_7);  
            break;                    // arg_int=1: Input dir with pullup high for I2C data pin
        case U8X8_MSG_GPIO_MENU_SELECT:
            u8x8_SetGPIOResult(u8x8, /* get menu select pin state */ 0);
            break;
        case U8X8_MSG_GPIO_MENU_NEXT:
            u8x8_SetGPIOResult(u8x8, /* get menu next pin state */ 0);
            break;
        case U8X8_MSG_GPIO_MENU_PREV:
            u8x8_SetGPIOResult(u8x8, /* get menu prev pin state */ 0);
            break;
        case U8X8_MSG_GPIO_MENU_HOME:
            u8x8_SetGPIOResult(u8x8, /* get menu home pin state */ 0);
            break;
        default:
            u8x8_SetGPIOResult(u8x8, 1); // default return value
            break;
    }
    return 1;
}

